package api

import (
	"errors"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/julienschmidt/httprouter"
	"learning.com/edu/models"
	"learning.com/edu/utils/helper"
	"learning.com/edu/utils/validator"
)

const (
	EmailPassword = iota // 邮箱密码
	EmailVerifyCode // 邮箱验证码
	PhonePassword // 手机密码登录
	PhoneVerifyCode // 手机验证码
)

/**
 * login:用户登录
 */
func (api *Api) login(w http.ResponseWriter, r *http.Request) {
	var input struct {
		Type 	int `json:"type"`
		Account string `json:"account"`
		Code    string `json:"code"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	typeValues := []int{
		EmailPassword,
		EmailVerifyCode,
		PhonePassword,
		PhoneVerifyCode,
	}

	// 验证
	v := validator.New()
	v.Check(validator.PermittedValue(input.Type, typeValues...), "type", "invalid type value")
	// 验证
	if input.Type == EmailPassword || input.Type == EmailVerifyCode {
		// 必须是有效的电子邮件地址
		v.Check(validator.Matches(input.Account, validator.EmailRX), 
			"account", "must be a valid email address")
	}
	if input.Type == PhonePassword || input.Type == PhoneVerifyCode {
		// 验证手机号码
		v.Check(validator.Matches(input.Account, validator.PhoneRX), 
			"account", "must be a valid phone number")
	}
	
	v.Check(input.Code != "", "Code", "must be provided")// 验证码不能为空
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	// 验证通过
	user, err := api.models.Users.GetUserBriefByAcountOrID(input.Account, 0)
	if err != nil {
		switch {
			// 账号不存在
			case errors.Is(err, helper.ErrRecordNotFound):
				if !(input.Type == EmailVerifyCode || input.Type == PhoneVerifyCode) {
					api.h.InvalidCredentialsResponse(w, r)
					return
				}

				var accountType string
				if input.Type == EmailVerifyCode {
					accountType = "email"
				} else {
					accountType = "phone_number"
				}
				user, err = api.models.Users.RegisterUser(input.Account, accountType)
				if err != nil {
					api.h.ServerErrorResponse(w, r, err)
					return
				}
			default:
				api.h.ServerErrorResponse(w, r, err)
				return
		}
	}

	// 密码未设置
	switch input.Type {
		case EmailPassword, PhonePassword:
			if user.PasswordHash == nil {
				api.h.ErrorResponse(w, r, http.StatusUnauthorized, "Password is Not Set")
				return
			}
			
			// 密码验证
			math, err := api.h.ComparePasswordAndHash(input.Code, user.PasswordHash)
			if err != nil {
				api.h.ServerErrorResponse(w, r, err)
				return
			} else if !math {
				api.h.InvalidCredentialsResponse(w, r)
				return
			}
		case EmailVerifyCode, PhoneVerifyCode:
			var codeType string
			if input.Type == EmailVerifyCode {
				codeType = models.VerifyCodeTypeEmail
			} else {
				codeType = models.VerifyCodeTypeSMS
			}
			scope := models.VerifyCodeScopeLogin
			ret, err := api.models.Users.VerifyCode(input.Code, codeType, scope, input.Account)
			if err != nil {
				api.h.ServerErrorResponse(w, r, err)
				return
			}
			if !ret {
				api.h.InvalidCredentialsResponse(w, r)
				return
			}
	}

	// 验证成功,生成token
	token, err := api.models.Tokens.New(user.ID, 30 * 24 *time.Hour, models.ScopeAuthentication)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
	data := map[string]interface{}{
		"token": token.PlainToken,
		"brief": user,
	}
	resp := api.h.NewResponse(0, data)
	err   = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * getUserProfile:获取用户详细信息
 */
func (api *Api) getUserProfile(w http.ResponseWriter, r *http.Request) {
	user := api.middleware.ContextGetUser(r)
	profile, err := api.models.Users.GetUserProfileById(user.ID)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	// 响应
	data := map[string]interface{}{
		"profile": profile,
	}

	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * logout:用户登出
 */
func (api *Api) logout(w http.ResponseWriter, r *http.Request) {
	authorizationHeader := r.Header.Get("Authorization")
	headerParts := strings.Split(authorizationHeader, " ")
	if len(headerParts) != 2 || headerParts[0] != "Bearer" {
		api.h.InvalidCredentialsResponse(w, r)
		return
	}

	// 删除token
	token := headerParts[1]
	err := api.models.Tokens.DeleteToken(token)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	data := map[string]interface{} {}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
}

const (
	AccountVerifyTypeEmail = "email"
	AccountVerifyTypePhoneNumber = "phone_number"
)

/**
 * verifyCodeForLogin:发送登录验证码
 */
func (api *Api) verifyCodeForLogin(w http.ResponseWriter, r *http.Request) {
	var input struct {
		Type    string `json:"type"`
		Account string `json:"account"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	// 验证
	typeValues := []string{
		AccountVerifyTypeEmail,
		AccountVerifyTypePhoneNumber,
	}

	var codeType string
	v := validator.New()
	v.Check(validator.PermittedValue(input.Type, typeValues...), "type", "invalid type value")
	// 验证
	if input.Type == AccountVerifyTypeEmail {
		v.Check(validator.Matches(input.Account, validator.EmailRX), "email", "must be a valid email address")
		codeType = models.VerifyCodeTypeEmail
	}
	if input.Type == AccountVerifyTypePhoneNumber {
		v.Check(validator.Matches(input.Account, validator.PhoneRX), "phone_number", "must be a valid phone number")
		codeType = models.VerifyCodeTypeSMS
	}

	// 验证不通过
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	// 验证账号是否存在
	userId, err := api.models.Users.GetAccountIdIfExists(input.Account)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	// 账号存在
	if userId != nil {
		// 生成验证码
		code, err := api.models.Users.GenerateVerifyCode(
			*userId, 
			codeType, 
			models.VerifyCodeScopeLogin, 
			input.Account,
			time.Minute * 30)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
			return
		}

		// 发送验证码
		api.h.Admin.Add(func() {
			data := map[string]interface{}{
				"code": code,
				"validMinutes": 30,
			}
			// 发送邮件
			if input.Type == AccountVerifyTypeEmail {
				err := api.mailer.Send(input.Account, "login_email.html", data)
				if err != nil {
					api.h.LogError(r, err)
				}
			}
			// TODO: 手机验证码发送
			// else TODO: Send verify code by phone
		})
	} else {
		// 注册部分的验证码
		// 生成验证码
		code, err := api.models.Users.GenerateVerifyCode(
			0, 
			codeType, 
			models.VerifyCodeScopeLogin, 
			input.Account,
			time.Minute * 30)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
			return
		}

		// 发送验证码
		api.h.Admin.Add(func() {
			data := map[string]interface{}{
				"code": code,
				"validMinutes": 30,
			}
			// 发送邮件
			if (input.Type == AccountVerifyTypeEmail) {
				err := api.mailer.Send(input.Account, "register_email.html", data)
				if err != nil {
					api.h.LogError(r, err)
				}
			}
			// TODO: 手机验证码发送
			// else TODO: Send verify code by phone
		})
	}

	data := map[string]interface{}{}
	resp := api.h.NewResponse(0, data)
	err  = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * getUserBrief:获取用户简略信息
 */
func (api *Api) getUserBrief(w http.ResponseWriter, r *http.Request) {
	user := api.middleware.ContextGetUser(r)
	brief, err := api.models.Users.GetUserBriefByAcountOrID("", user.ID)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	data := map[string]interface{}{
		"brief": brief,
	}

	resp := api.h.NewResponse(0, data)
	err  = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * getUserAvatar:获取用户头像
 */
func (api *Api) getUserAvatar(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	avatar := api.h.ReadString(query, "k", "")
	if avatar == "" {
		user := api.middleware.ContextGetUser(r)
		avatarDB, err := api.models.Users.GetUserAvatarById(user.ID)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
			return
		}
		if avatarDB == nil {
			api.h.NotFoundResponse(w, r)
			return
		}
		avatar = *avatarDB
	}
	filepath := filepath.Join(api.config.Path.AvatarsPath(), avatar)
	http.ServeFile(w, r, filepath)
}

/**
 * updateAvatar:修改用户头像
 */
func (api *Api) updateUserAvatar(w http.ResponseWriter, r *http.Request) {
	user := api.middleware.ContextGetUser(r)
	err := r.ParseMultipartForm(10 << 20)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	file, handler, err := r.FormFile("avatar")
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}
	defer file.Close()

	// 保存到本地
	dstExt := filepath.Ext(handler.Filename)
	dstName := api.h.RandomString(16) + dstExt
	dstPath := filepath.Join(api.config.Path.AvatarsPath(), dstName)
	dst, err := os.Create(dstPath)

	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
	defer dst.Close()

	if _, err = io.Copy(dst, file); err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	// 更新数据库
	old, err := api.models.Users.UpdateUserAvatar(user.ID, dstName)
	if err != nil {
		os.Remove(dstPath)
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	if (old != "" && old != "default_avatar.png") {
		// 删除旧头像
		os.Remove(filepath.Join(api.config.Path.AvatarsPath(), old))
	}

	// 响应
	var output = struct {
		ID 	   int64 `json:"id"`
		Field  string `json:"field"`
		Avatar string `json:"value"`
	}{
		ID: user.ID,
		Field: "avatar",
		Avatar: dstName,
	}
	
	data := map[string]interface{} {
		"result": output,
	}

	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 修改用户昵称
 */
func (api *Api) updateUserNickName(w http.ResponseWriter, r *http.Request) { 
	var input struct {
		Nickname string `json:"nickname"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	user := api.middleware.ContextGetUser(r)
	err = api.models.Users.UpdateUserNickName(user.ID, input.Nickname)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	var output = struct {
		ID       int64  `json:"id"`
		Field    string `json:"field"`
		Nickname string `json:"value"`
	} {
		ID: user.ID,
		Field: "nickname",
		Nickname: input.Nickname,
	}

	data := map[string]interface{} {
		"result": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 修改用户职业
 */
func (api *Api) updateUserProfession(w http.ResponseWriter, r *http.Request)()  {
	var input struct {
		ProfessionID int64 `json:"profession_id"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	user := api.middleware.ContextGetUser(r)
	newTitle, err := api.models.Users.UpdateUserProfession(user.ID, input.ProfessionID)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	var output = struct {
		ID        int64  `json:"id"`
		Field     string `json:"field"`
		Professon string `json:"value"`
	} {
		ID: user.ID,
		Field: "profession",
		Professon: newTitle,
	}

	data := map[string]interface{} {
		"result": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 修改用户性别
 */
func (api *Api) updateUserGender(w http.ResponseWriter, r *http.Request)()  {
	var input struct {
		Gender string `json:"gender"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	genderValues := []string{"m", "f"}
	v := validator.New()
	v.Check(validator.PermittedValue(input.Gender, genderValues...), "type", "invalid gender value")
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	user := api.middleware.ContextGetUser(r)
	err = api.models.Users.UpdateUserGender(user.ID, input.Gender)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	var output = struct {
		ID       int64  `json:"id"`
		Field    string `json:"field"`
		Gender   string `json:"value"`
	} {
		ID: user.ID,
		Field: "gender",
		Gender: input.Gender,
	}

	data := map[string]interface{} {
		"result": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 修改用户地区或地址
 */
func (api *Api) updateUserRegion(w http.ResponseWriter, r *http.Request)()  {
	var input struct {
		Code string `json:"code"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	code, err := strconv.ParseInt(input.Code, 10, 64)
	if err != nil || code < 0 {
		api.h.BadRequestResponse(w, r, errors.New("invalid code parameter"))
		return
	}

	user := api.middleware.ContextGetUser(r)

	region, regionCodes, err := api.models.Users.UpdateUserRegion(user.ID, code)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	var output = struct {
		ID            int64  `json:"id"`
		Field         string `json:"field"`
		Region        string `json:"value"`
		RegionCodes []string `json:"list"`
	} {
		ID: user.ID,
		Field: "region",
		Region: region,
		RegionCodes: regionCodes,
	}

	data := map[string]interface{} {
		"result": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 修改用户签名
 */
func (api *Api) updateUserIntroduction(w http.ResponseWriter, r *http.Request)()  {
	var input struct {
		Introduction string `json:"introduction"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	user := api.middleware.ContextGetUser(r)
	err = api.models.Users.UpdateUserIntroduction(user.ID, input.Introduction)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	var output = struct {
		ID           int64  `json:"id"`
		Field        string `json:"field"`
		Introduction string `json:"value"`
	} {
		ID: user.ID,
		Field: "introduction",
		Introduction: input.Introduction,
	}

	data := map[string]interface{} {
		"result": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 获取所有职业信息
 */
func (api *Api) getAllProfessions(w http.ResponseWriter, r *http.Request) {
	professions, err := api.models.Users.GetAllProfessions()
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	data := map[string]interface{} {
		"professions": professions,
	}

	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 获取中国地区信息
 */
func (api *Api) getChinaRegions(w http.ResponseWriter, r *http.Request) {
	qs := r.URL.Query()
	code := api.h.ReadInt64(qs, "code", 0, nil)
	regions, err := api.models.Users.GetChinaRegionByCode(code)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	data := map[string]interface{} {
		"regions": regions,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 更新用户邮箱
 */
func (api *Api) changeUserEmail(w http.ResponseWriter, r *http.Request) {
	var input struct {
		Email string `json:"email"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	v := validator.New()
	v.Check(validator.Matches(input.Email, validator.EmailRX), "email", "请输入有效邮箱")
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	exists, err := api.models.Users.IsEmailExists(input.Email)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	if exists {
		var output = struct {
			Type 	string `json:"type"`
			Status 	int32 `json:"status"`
		} {
			Type: "email",
			Status: 1,
		}

		data := map[string]interface{} {
			"result": output,
		}
		resp := api.h.NewResponse(0, data)
		err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
		}
		return
	}

	// 不存在,生成验证码
	user := api.middleware.ContextGetUser(r)

	code, err := api.models.Users.GenerateVerifyCode(
		user.ID, 
		models.VerifyCodeTypeEmail, 
		models.VerifyCodeScopeResetEmail,
		input.Email, 
		30*time.Minute,
	)
	
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	// 添加后台任务
	api.h.Admin.Add(func() {
		data := map[string]interface{} {
			"code": code,
			"validMinutes": "30",
		}
		err := api.mailer.Send(input.Email, "reset_email.html", data)
		if err != nil {
			api.h.LogError(r, err)
		}
	})

	// 返回结果
	var output = struct {
		Type         string  `json:"type"`
		Status    	 int32 `json:"status"`
		ValidMinutes int32 `json:"valid_minutes"`
	} {
		Type: "email",
		Status: 0,
		ValidMinutes: 30,
	}
	data := map[string]interface{} {
		"status": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 重置用户邮箱
 */
func (api *Api) updateUserEmail(w http.ResponseWriter, r *http.Request) { 
	var input struct {
		Email string `json:"email"`
		VerifyCode string `json:"verify_code"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	v := validator.New()
	v.Check(validator.Matches(input.Email, validator.EmailRX), "email", "请输入有效邮箱")
	v.Check(input.VerifyCode != "", "verify_code", "请输入有效验证码")
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	user := api.middleware.ContextGetUser(r)
	// 获取验证码
	reset, err := api.models.Users.VerifyCode(
		input.VerifyCode, 
		models.VerifyCodeTypeEmail, 
		models.VerifyCodeScopeResetEmail, 
		input.Email,
	)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
	// 验证码不正确
	if !reset {
		api.h.ErrorResponse(w, r, http.StatusBadRequest, "验证码错误")
		return
	}

	// 修改用户邮箱
	err = api.models.Users.UpdateUserEmail(user.ID, input.Email)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	var output = struct {
		ID           int64  `json:"id"`
		Field        string `json:"field"`
		Email        string `json:"value"`
	} {
		ID: user.ID,
		Field: "email",
		Email: input.Email,
	}
	data := map[string]interface{} {
		"result": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 更新用户手机号码
 */
func (api *Api) changeUserPhoneNumber(w http.ResponseWriter, r *http.Request) {
	var input struct {
		PhoneNumber string `json:"phone_number"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	v := validator.New()
	v.Check(validator.Matches(input.PhoneNumber, validator.PhoneRX), "phone_number", "请输入有效手机号码")
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	// 验证手机号是否存在
	exists, err := api.models.Users.IsPhoneNumberExists(input.PhoneNumber)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
	if exists {
		var output = struct {
			Type 	string `json:"type"`
			Status 	int32 `json:"status"`
		} {
			Type: "phone_number",
			Status: 1,
		}

		data := map[string]interface{} {
			"result": output,
		}
		resp := api.h.NewResponse(0, data)
		err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
		}
		return
	}

	user := api.middleware.ContextGetUser(r)
	_, err = api.models.Users.GenerateVerifyCode(
		user.ID, 
		models.VerifyCodeTypeSMS, 
		models.VerifyCodeScopeResetPhoneNumber, 
		input.PhoneNumber,
		30*time.Minute,
	)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	// 添加后台任务
	api.h.Admin.Add(func() {
		// TODO: 短信
		// data := map[string]interface{} {
		// 	"phone_number": input.PhoneNumber,
		// 	"valid_minutes": "30",
		// }
		// err := api.sms.Send(input.PhoneNumber, "reset_phone_number.html", data)
		// if err != nil {
		// 	api.h.LogError(r, err)
		// }
	})

	var output = struct {
		Type         string  `json:"type"`
		Status    	 int32 `json:"status"`
		ValidMinutes int32 `json:"valid_minutes"`
	} {
		Type: "phone_number",
		Status: 0,
		ValidMinutes: 30,
	}
	data := map[string]interface{} {
		"status": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 修改用户手机
 */
func (api *Api) updateUserPhoneNumber(w http.ResponseWriter, r *http.Request) { 
	var input struct {
		PhoneNumber string `json:"phone_number"`
		VerifyCode string `json:"verify_code"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	v := validator.New()
	v.Check(validator.Matches(input.PhoneNumber, validator.PhoneRX), "phone_number", "请输入有效手机号码")
	v.Check(input.VerifyCode != "", "verify_code", "请输入有效验证码")
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	user := api.middleware.ContextGetUser(r)

	reset, err := api.models.Users.VerifyCode(
		input.VerifyCode, 
		models.VerifyCodeTypeSMS, 
		models.VerifyCodeScopeResetPhoneNumber, 
		input.PhoneNumber,
	)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	if !reset {
		api.h.ErrorResponse(w, r, http.StatusBadRequest, "验证码错误")
		return
	}

	err = api.models.Users.UpdateUserPhoneNumber(user.ID, input.PhoneNumber)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
	var output = struct {
		ID           int64  `json:"id"`
		Field        string `json:"field"`
		PhoneNumber  string `json:"value"`
	} {
		ID: user.ID,
		Field: "phone_number",
		PhoneNumber: input.PhoneNumber,
	}
	data := map[string]interface{} {
		"result": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 更新用户密码
 */
func (api *Api) changeUserPassword(w http.ResponseWriter, r *http.Request) {
	var input struct {
		Type string `json:"type"`
		Account string `json:"account"`
	}

	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	var codeType string
	v := validator.New()
	switch input.Type {
		case "email":
			v.Check(validator.Matches(input.Account, validator.EmailRX), 
				"account", "请输入有效邮箱")
			codeType = models.VerifyCodeTypeEmail
		case "phone_number":
			v.Check(validator.Matches(input.Account, validator.PhoneRX), "account", 
				"请输入有效手机号码");
			codeType = models.VerifyCodeTypeSMS
		default:
			api.h.ErrorResponse(w, r, http.StatusBadRequest, "无效的账户类型")
			return
	}

	if(!v.Valid()) {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	// 判断用户是否存在
	userId, err := api.models.Users.GetAccountIdIfExists(input.Account)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
	if userId == nil {
		api.h.ErrorResponse(w, r, http.StatusBadRequest, "用户不存在")
		return
	}

	code, err := api.models.Users.GenerateVerifyCode(
		*userId, 
		codeType, 
		models.VerifyCodeScopeResetPassword, 
		input.Account,
		30*time.Minute,
	)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}
	if code == "" {
		api.h.ErrorResponse(w, r, http.StatusBadRequest, "验证码生成失败")
		return
	}

	api.h.Admin.Add(func() {
		switch codeType {
			case models.VerifyCodeTypeEmail:
				data := map[string]interface{} {
					"email": input.Account,
					"valid_minutes": "30",
				}
				err := api.mailer.Send(input.Account, "reset_password.tmpl", data)
				if err != nil {
					api.h.LogError(r, err)
				}
			case models.VerifyCodeTypeSMS:
				// TODO: 短信
		}
	})

	var output = struct {
		Type         string  `json:"type"`
		Status       int32   `json:"status"`
		ValidMinutes int32   `json:"valid_minutes"`
	} {
		Type: input.Type,
		Status: 0,
		ValidMinutes: 30,
	}

	data := map[string]interface{} {
		"status": output,
	}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * 重置用户密码
 */

const (
	PasswordVerifyTypeOldPassword = "old_password"
	PasswordVerifyTypeEmailVerifyCode = "email_code"
	PasswordVerifyTypePhoneNumberVerifyCode = "phone_number_code"
)

func (api *Api) updateUserPassword(w http.ResponseWriter, r *http.Request) { 
	var input struct {
		Type 	 string `json:"type"`
		Account  string `json:"account"`
		Password string `json:"password"`
		Code 	 string `json:"code"`
	}
	err := api.h.ReadJSON(w, r, &input)
	if err != nil {
		api.h.BadRequestResponse(w, r, err)
		return
	}

	permittedTypes := []string {
		PasswordVerifyTypeOldPassword,
		PasswordVerifyTypeEmailVerifyCode,
		PasswordVerifyTypePhoneNumberVerifyCode,
	}

	v := validator.New()
	v.Check(validator.PermittedValue(input.Type, permittedTypes...), 
		"type", 
		"无效的验证方式",
	)
	
	// 验证方式
	switch input.Type {
		case PasswordVerifyTypeEmailVerifyCode:
			v.Check(validator.Matches(input.Account, validator.EmailRX), 
				"email", 
				"请输入有效邮箱")
		case PasswordVerifyTypePhoneNumberVerifyCode:
			v.Check(validator.Matches(input.Account, validator.PhoneRX), 
				"phone_number", 
				"请输入有效手机号码")
	}
	v.Check(len(input.Password) >= 8, "password", "密码长度不能小于8个字符")
	v.Check(len(input.Password) <= 20, "password", "密码长度不能大于20个字符")
	v.Check(input.Code != "", "code", "验证码不能为空")
	if !v.Valid() {
		api.h.FailedValidationResponse(w, r, v.Errors)
		return
	}

	codeType := ""
	switch input.Type {
		case PasswordVerifyTypeEmailVerifyCode:
			codeType = models.VerifyCodeTypeEmail
		case PasswordVerifyTypePhoneNumberVerifyCode:
			codeType = models.VerifyCodeTypeSMS
	}
	
	var userId int64
	var valid bool
	if codeType != "" {
		// 新密码
		// 验证用户是否存在
		id, err := api.models.Users.GetAccountIdIfExists(input.Account)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
			return
		}
		if id == nil {
			api.h.ErrorResponse(w, r, http.StatusBadRequest, "用户不存在")
			return
		}
		userId = *id

		scope := models.VerifyCodeScopeResetPassword
		valid, err = api.models.Users.VerifyCode(
			input.Code, 
			codeType, 
			scope, 
			input.Account,
		)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
			return
		}
		if !valid {
			api.h.ErrorResponse(w, r, http.StatusBadRequest, "验证码错误")
			return
		}
	} else {
		// 旧密码
		user := api.middleware.ContextGetUser(r)
		if user.IsAnonymous() {
			api.h.InvalidCredentialsResponse(w, r)
			return
		}
		// 获取用户信息
		brief, err := api.models.Users.GetUserBriefByAcountOrID("", user.ID)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
			return
		}
		// 验证密码
		if brief.PasswordHash == nil {
			api.h.ErrorResponse(w, r, http.StatusUnauthorized, "用户未设置密码")
			return
		}
		// 验证旧密码
		valid, err = api.h.ComparePasswordAndHash(input.Code, brief.PasswordHash)
		if err != nil {
			api.h.ServerErrorResponse(w, r, err)
			return
		}
		if !valid {
			api.h.ErrorResponse(w, r, http.StatusUnauthorized, "密码错误");
			return
		}
		userId = user.ID
	}

	// 产生新的密码哈希
	hash, err := api.h.GeneratePasswordHash(input.Password)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	// 更新密码
	err = api.models.Users.UpdateUserPassword(userId, hash)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
		return
	}

	// 响应
	data := map[string]interface{} {}
	resp := api.h.NewResponse(0, data)
	err = api.h.WriteJSON(w, http.StatusOK, resp, nil)
	if err != nil {
		api.h.ServerErrorResponse(w, r, err)
	}
}

/**
 * userRoutes:用户相关路由注册
 */
func (api *Api) userRoutes(router *httprouter.Router) {
	// 登录
	router.HandlerFunc(http.MethodPost, "/v1/user/login", api.login)

	// 查询用户详细信息
	router.HandlerFunc(http.MethodGet, "/v1/user/profile", 
		api.middleware.RequireAuthenticatedUser(api.getUserProfile))

	// 登出
	router.HandlerFunc(http.MethodPost, "/v1/user/logout", 
		api.middleware.RequireAuthenticatedUser(api.logout))

	// 获取验证码
	router.HandlerFunc(http.MethodPost, "/v1/user/login/verify_code",
		api.verifyCodeForLogin)
	
	// 获取用户简略信息
	router.HandlerFunc(http.MethodGet, "/v1/user/brief", 
		api.middleware.RequireAuthenticatedUser(api.getUserBrief))
	
	// 获取用户头像
	router.HandlerFunc(http.MethodGet, "/v1/user/avatar", api.getUserAvatar)

	// 修改用户头像
	router.HandlerFunc(http.MethodPatch, "/v1/user/avatar", 
		api.middleware.RequireAuthenticatedUser(api.updateUserAvatar))
		
	// 修改用户昵称
	router.HandlerFunc(http.MethodPatch, "/v1/user/nickname",
		api.middleware.RequireAuthenticatedUser(api.updateUserNickName),
	)

	// 修改用户职业
	router.HandlerFunc(http.MethodPatch, "/v1/user/profession",
		api.middleware.RequireAuthenticatedUser(api.updateUserProfession),
	)
	router.HandlerFunc(http.MethodGet, "/v1/user/professions",
		api.middleware.RequireAuthenticatedUser(api.getAllProfessions),
	)

	// 修改用户性别
	router.HandlerFunc(http.MethodPatch, "/v1/user/gender",
		api.middleware.RequireAuthenticatedUser(api.updateUserGender),
	)

	// 修改用户地区或地址
	router.HandlerFunc(http.MethodPatch, "/v1/user/region",
		api.middleware.RequireAuthenticatedUser(api.updateUserRegion),
	)

	router.HandlerFunc(http.MethodGet, "/v1/user/chinaregions",
		api.middleware.RequireAuthenticatedUser(api.getChinaRegions),
	)

	// 修改用户签名
	router.HandlerFunc(http.MethodPatch, "/v1/user/introduction",
		api.middleware.RequireAuthenticatedUser(api.updateUserIntroduction),
	)

	// 更新用户邮箱
	router.HandlerFunc(http.MethodPost, "/v1/user/email/change",
		api.middleware.RequireAuthenticatedUser(api.changeUserEmail),
	)

	// 修改用户邮箱
	router.HandlerFunc(http.MethodPatch, "/v1/user/email",
		api.middleware.RequireAuthenticatedUser(api.updateUserEmail),
	)

	// 更新用户邮箱
	router.HandlerFunc(http.MethodPost, "/v1/user/phone_number/change",
		api.middleware.RequireAuthenticatedUser(api.changeUserPhoneNumber),
	)

	// 修改用户手机号
	router.HandlerFunc(http.MethodPatch, "/v1/user/phone_number",
		api.middleware.RequireAuthenticatedUser(api.updateUserPhoneNumber),
	)

	// 更新用户密码
	router.HandlerFunc(http.MethodPost, "/v1/user/password/change", 
		api.changeUserPassword)

	// 重置用户密码
	router.HandlerFunc(http.MethodPatch, "/v1/user/password", 
		api.updateUserPassword)
}